package core

import (
	"google.golang.org/grpc"
	"net/http"
	"sync"
)

type (
	MiddlewareFunc func(*Context) bool
	HandlerFunc    func(*Context)
)

type Gateway struct {
	Router      *Router
	middlewares []MiddlewareFunc
	httpServer  *http.Server
	grpcClients map[string]*grpc.ClientConn
	mu          sync.RWMutex
}

func New() *Gateway {
	return &Gateway{
		Router:      NewRouter(),
		grpcClients: make(map[string]*grpc.ClientConn),
	}
}

func (g *Gateway) Use(middleware MiddlewareFunc) {
	g.middlewares = append(g.middlewares, middleware)
}

func (g *Gateway) Run(addr string) error {
	g.httpServer = &http.Server{
		Addr:    addr,
		Handler: g.buildHandler(),
	}
	return g.httpServer.ListenAndServe()
}

func (g *Gateway) buildHandler() http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		ctx := NewContext(w, r, g)
		chain := append(g.middlewares, g.Router.Handle)
		for _, middleware := range chain {
			if !middleware(ctx) {
				return
			}
		}
	})
}

func (g *Gateway) GetGRPCConn(addr string) (*grpc.ClientConn, error) {
	g.mu.RLock()
	if conn, ok := g.grpcClients[addr]; ok {
		g.mu.RUnlock()
		return conn, nil
	}
	g.mu.RUnlock()

	g.mu.Lock()
	defer g.mu.Unlock()
	conn, err := grpc.Dial(addr, grpc.WithInsecure())
	if err == nil {
		g.grpcClients[addr] = conn
	}
	return conn, err
}
